#include <OpenNetwork/OpenNetwork.h>

#include "CUDPPort.h"
#include "CTCPPort.h"

#ifdef WINDOWS
#include <winsock2.h>
#include <Ws2tcpip.h>
#define WIN32_LEAN_AND_MEAN
#define ONET_EXPORT __declspec(dllexport)
#include <windows.h>
#elif defined(__unix__)
#define ONET_EXPORT
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <netdb.h>
#else
#error "What's your operating system?"
#endif

/*!
    Copright: Kai "DrHalan" Mast - www.freakybytes.org

    This file is part of OpenNetwork. See OpenNetwork.h for license information.
*/

namespace onet
{
    ONET_EXPORT Checksum createChecksum(std::string data, EChecksumType type)
    {
        if(type == ECT_SHA1)
        {
            return data;
        }
        else
            return "";
    }

    ONET_EXPORT INetworkPort* createNetworkPort(EPortType type, INetworkCallback* callback, u16 maxPeers, u16 port, bool listen, bool IPv6)
    {
        if (type == EPT_UserDatagramProtocol)
            return dynamic_cast<INetworkPort*>(new CUDPPort(callback, maxPeers, port, listen, IPv6));
        else if (type == EPT_TransmissionControlProtocol)
            return dynamic_cast<INetworkPort*>(new CTCPPort(callback, maxPeers, port, listen, IPv6));
        else
            return 0;
    }

    ONET_EXPORT u32 getLocalTime(void)
    {
#ifdef WINDOWS
        return GetTickCount();
#else
        timespec buffer;
        clock_gettime(CLOCK_MONOTONIC, &buffer);

        u32 time = buffer.tv_sec * 1000;
        time += buffer.tv_nsec/1000000.0;

        return time;
#endif
    }

    //! Sleep (in ms)
    ONET_EXPORT void sleep(u32 time)
    {
#ifdef WINDOWS
        sleep(time/1000);
#else
        s32 seconds = 0;

        if (time > 1000)
        {
            seconds = s32(time/1000);
            sleep(seconds);
        }

        //! The Usleep functions takes a maximum of 1,000,000 MICROseconds
        usleep((time - seconds)*1000);
#endif
    }

    ONET_EXPORT SNetworkAddress createAddressFromURL(const std::string url, const u16 port)
    {
        struct addrinfo * host;

        s32 error = getaddrinfo(url.c_str(), 0, 0, &host );

        if (error)
        {
            std::cout << "Error getting address: " <<  gai_strerror( error ) << std::endl;

            return SNetworkAddress();
        }

        SNetworkAddress address;

        //!Setup the port
        address.portNumber = port;

        //!Check wheter its IPv6 or not
        if (host->ai_family == AF_INET6)
        {
            c8 ipAsString[25];
            inet_ntop( AF_INET6, &((sockaddr_in6*)host->ai_addr)->sin6_addr, &ipAsString[0], 25);
            address.ip = std::string(&ipAsString[0]);

            address.IPv6 = true;
        }
        else
        {
            c8 ipAsString[16];
            inet_ntop( AF_INET, &((sockaddr_in*)host->ai_addr)->sin_addr, &ipAsString[0], 16);
            address.ip = std::string(&ipAsString[0]);

            address.IPv6 = false;
        }

        freeaddrinfo(host);

        return address;
    }

    //! This converts the NetworkInformation-Enumeration to a string
    ONET_EXPORT std::string networkInformationToString(ENetworkInformation info)
    {
        if(info == ENI_ConnectionDenied)
            return "Connection request was denied";
        else if(info == ENI_ConnectionTimeout)
            return "Connection timed out";
        else if(info == ENI_ConnectionClosed)
            return "Connection was closed";
        else
            return "Unknown error";
    }

    ONET_EXPORT bool start(void)
    {
#ifdef WINDOWS
        WSAData wsaData;

        if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
            return false;
        else
            return true;
#else
        return true;
#endif
    }

    ONET_EXPORT void end(void)
    {
#ifdef WINDOWS
        WSACleanup();
#endif
    }

}
